#include "pacman.h"

namespace Lio {

void drawImg(const Image * img, double x, double y)
{
	glRasterPos2d(x,y);
	glDrawPixels(	img->width,img->height,
					getPixelFormat(img),GL_UNSIGNED_BYTE,
					img->data);
}
BOOL flipImageH(const Image *imgSrc, Image *imgDst)
{
	int w,h,i,j;

	if(!imgDst->setSize(imgSrc->width,imgSrc->height,imgSrc->comps))
		return FALSE;
	for(h=0;h<IMG_H;h++)
		for(w=0;w<IMG_W;w++)
		{
			i=PIXEL_COMP_RGBA*(h*IMG_W+w);
			j=PIXEL_COMP_RGBA*(h*IMG_W+IMG_W-w-1);
			memcpy(imgDst->data+i,imgSrc->data+j,PIXEL_COMP_RGBA);
		}
	return FALSE;
}
const WORD Pacman::bonuses[BONUS_COUNT]={
	1,//BONUS_POINT
	0,//BONUS_BIG_POINT
	10,//BONUS_MONSTER
	20//BONUS_CHERRY
};
const WORD Pacman::params[CREATURE_COUNT][STATE_COUNT][PARAM_COUNT]={
	{//CREATURE_MONST:
		//PARAM_ANIM, PARAM_TIME, PARAM_SPEED, PARAM_WAVE_A, PARAM_WAVE_W
		{2,				400,			80,				10,				2}, //STATE_WEAK,
		{2,				300,			90,				5,				4}, //STATE_STRONG,
		{1,				0,				100,			20,				6} // STATE_DEAD,
	},
	{//CREATURE_PACKMAN:
		{3,				150,			100,			0,				0}, //STATE_WEAK,
		{3,				100,			120,			0,				0}, //STATE_STRONG,
		{1,				2000,			0,				0,				0} // STATE_DEAD,
	}
};
const MonsterInfo Pacman::monstInfo[MONST_COUNT]={
//	color,			speed,	wait
	{0xffff00ff,	0.75,	8000},
	{0xff0000ff,	0.85,	6000},
	{0xff00ff00,	0.95,	4000},
	{0xffffff00,	1.05,	2000}
};
const double Pacman::susTimes[SUSPENSE_COUNT]={
	0,//SUSPENSE_NONE
	2.3,//SUSPENSE_DEAD
	3.2,//SUSPENSE_LEVEL
};
void Pacman::growPotMap(DWORD w, DWORD h, BYTE id, WORD pot)
{
	if(!check(w,h)||(maps[id][h][w]&&maps[id][h][w]<=pot))
		return;
	mapop++;
	maps[id][h][w]=pot++;
	growPotMap(w+1,h,id,pot);
	growPotMap(w-1,h,id,pot);
	growPotMap(w,h+1,id,pot);
	growPotMap(w,h-1,id,pot);
}
BOOL Pacman::setDir(Creature &monster, BYTE id, BOOL toMin)
{
	WORD	w=monster.loc.x,h=monster.loc.y,
			dh[4]={1,-1,0,0},dw[4]={0,0,1,-1},
			wi,hi,pot=maps[id][h][w],poti;
	int i;
	double dx,dy;
	char newDirX=0,newDirY=0;

	if(	monster.state==STATE_STRONG&&
		abs(pacman.pos.x-monster.pos.x)<IMG_W&&
		abs(pacman.pos.y-monster.pos.y)<IMG_H)
	{
		monster.target.copy(pacman.pos);
	}
	else
	{
		for(i=0;i<4;i++)
		{
			wi=monster.loc.x+dw[i];
			hi=monster.loc.y+dh[i];
			if(wi<0||wi>=LEV_W||hi<0||hi>=LEV_H)
				continue;
			poti=maps[id][hi][wi];
			if(poti&&((toMin&&pot>poti)||(!toMin&&pot<poti)))
			{
				pot=poti;
				w=monster.loc.x+dw[i];
				h=monster.loc.y+dh[i];
			}
		}
		monster.target.set(levPixW[w],levPixH[h]);
	}
	dx=monster.target.x-monster.pos.x;
	dy=monster.target.y-monster.pos.y;
	if(dx||dy)
	{
		newDirX=(abs(dx)>=ENTRY_TOLER)?((char)(dx/abs(dx))):0;
		newDirY=(abs(dy)>=ENTRY_TOLER)?((char)(dy/abs(dy))):0;
	}
	monster.setDir(newDirX,newDirY);
	return newDirX||newDirY;
}
void Creature::setDir(char newDirX, char newDirY)
{
	if(newDirX>0)
		orient=ORIENT_RIGHT;
	else if(newDirX<0)
		orient=ORIENT_LEFT;
	dirx=newDirX;
	diry=newDirY;
}
BOOL Creature::move(DWORD ms)
{
	double d=0,bound=0,cx,cy,x,y;
	WORD w,h;

	time+=ms;
	if(waveA&&waveW)
		wave.y=waveA*sin(waveW*time+wavePhi);
	if(time<susTime||(!dirx&&!diry))
		return FALSE;
	w=loc.x;
	h=loc.y;
	x=pos.x;
	y=pos.y;
	cx=parent->levPixW[w];
	cy=parent->levPixH[h];
	timer+=ms;
	if(animCount>1&&timer>=animTime)
	{
		timer-=animTime;
		if(animInc)
		{
			anim++;
			if(anim==animCount-1)
				animInc=FALSE;
		}
		else
		{
			anim--;
			if(anim==0)
				animInc=TRUE;
		}
	}
	d=speed*ms;
	if(dirx>0)
	{
		bound=cx+((w<WIN_W-1)?ENTRY_TOLER:0);
		if(x+d>=bound&&(!parent->check(w+1,h)||abs(y-cy)>ENTRY_TOLER))
		{
			x=bound;
			if(parent->check(w+1,h)&&!diry)
			{
				if(y>cy)
					y-=d;
				else if(y<cy)
					y+=d;
			}
		}
		else
			x+=d;
	}
	else if(dirx<0)
	{
		bound=cx-((w>0)?ENTRY_TOLER:0);
		if(x-d<=bound&&(!parent->check(w-1,h)||abs(y-cy)>ENTRY_TOLER))
		{
			x=bound;
			if(parent->check(w-1,h)&&!diry)
			{
				if(y>cy)
					y-=d;
				else if(y<cy)
					y+=d;
			}
		}
		else
			x-=d;
	}
	if(diry<0)
	{
		bound=cy-((h>0)?ENTRY_TOLER:0);
		if(y-d<=bound&&(!parent->check(w,h-1)||abs(x-cx)>ENTRY_TOLER))
		{
			y=bound;
			if(parent->check(w,h-1)&&!dirx)
			{
				if(x>cx)
					x-=d;
				else if(x<cx)
					x+=d;
			}
		}
		else
			y-=d;
	}
	else if(diry>0)
	{
		bound=cy+((h<WIN_H-1)?ENTRY_TOLER:0);
		if(y+d>=bound&&(!parent->check(w,h+1)||abs(x-cx)>ENTRY_TOLER))
		{
			y=bound;
			if(parent->check(w,h+1)&&!dirx)
			{
				if(x>cx)
					x-=d;
				else if(x<cx)
					x+=d;
			}
		}
		else
			y+=d;
	}
	pos.set(x,y);
	loc.set(PIX_TO_LEV_X(x),PIX_TO_LEV_Y(y));
	return (w!=loc.x||h!=loc.y);
}
void Creature::init(const Pacman * parent, BYTE id, BYTE state, double speedCoef, WORD waitStartTime)
{
	this->parent=parent;
	this->id=id;
	time=0;
	kSpeed=speedCoef;
	wavePhi=random(-PI,PI);
	susTime=waitStartTime;
	setState(state);
}
void Creature::setState(BYTE state)
{
	const WORD *params;
	this->state=state;
	params=Pacman::params[id][state];
	anim=0;
	timer=0;
	animInc=TRUE;
	speed=1e-3*kSpeed*params[PARAM_SPEED];
	animCount=params[PARAM_ANIM];
	animTime=params[PARAM_TIME];
	waveA=params[PARAM_WAVE_A];
	waveW=1e-3*kSpeed*params[PARAM_WAVE_W];
}
BOOL Pacman::create()				
{
	int state,anim,i,animSize,w,h;
	Image *img;
	InitInfo info;
	Tokenizer tok;
	Tokenizer::Object token;
	File file;
	NumberFormat format;

	Application::create();
	// window name:
	name=TITLE_PACMAN;
	// setup the display information: 
	display.bpp=BPP;
	display.width=WIN_W;
	display.height=WIN_H;

	font.setFace(_T("Courier New"));
	font.height=-FONT_SIZE;
	font.bold=TRUE;

	suspense=SUSPENSE_NONE;
	susTime=0;
	levIndex=0;
	loops=1;
	runTime=0;
	score=0;
	highScore=0;
	lives=LIFE_COUNT;

	if(!info.read(INIT_FILE_NAME,_T("="),_T(";"),_T(" \t\n")))
		return FALSE;
	levCount=info.getInt(_T("level_count"),0);
	if(levCount<=0||!levels.setSize(levCount))
		return FALSE;
	for(i=0;i<levCount;i++)
	{
		_stprintf(strbuf,_T("level_%d"),i+1);
		tok.set(info.get(strbuf));
		if(tok.length!=LEV_W*LEV_H)
			return FALSE;
		for(h=LEV_H-1,token=tok.first;h>=0;h--)
			for(w=0;w<LEV_W;w++,token=token->next)
				levels[i][h][w]=format.toInt(token->value);
	}
	file.open(SCORE_FILE_NAME);
	file.read(&highScore);
	file.close();

	// Build navigation
	for(i=0;i<LEV_W;i++)
		levPixW[i]=LEV_TO_PIX_X(i);
	for(i=0;i<LEV_H;i++)
		levPixH[i]=LEV_TO_PIX_Y(i);
	// Load bitmaps
	for(i=1;i<IMG_COUNT;i++)
	{
		if(!loadImg(i+IMG_FILE_ID))
			return FALSE;
		buildImg(imgs+i);
	}
	for(state=0;state<STATE_COUNT;state++)
	{
		animSize=params[CREATURE_PACKMAN][state][PARAM_ANIM];
		pacman.imgs[state][ORIENT_RIGHT].setSize(animSize);
		pacman.imgs[state][ORIENT_LEFT].setSize(animSize);
		for(anim=0;anim<animSize;anim++)
		{
			if(!loadImg(getImgID(CREATURE_PACKMAN,state,anim)))
				return FALSE;
			img=pacman.imgs[state][ORIENT_RIGHT].elems+anim;
			buildImg(img);
			flipImageH(img,pacman.imgs[state][ORIENT_LEFT].elems+anim);
		}
		animSize=params[CREATURE_MONST][state][PARAM_ANIM];
		for(i=0;i<MONST_COUNT;i++)
		{
			monst[i].imgs[state][ORIENT_RIGHT].setSize(animSize);
			monst[i].imgs[state][ORIENT_LEFT].setSize(animSize);
		}
		for(anim=0;anim<animSize;anim++)
		{
			if(!loadImg(getImgID(CREATURE_MONST,state,anim)))
				return FALSE;
			for(i=0;i<MONST_COUNT;i++)
			{
				img=monst[i].imgs[state][ORIENT_RIGHT].elems+anim;
				buildImg(img,TRANSP_COLOR,CHANGE_COLOR,monstInfo[i].color);
				flipImageH(img,monst[i].imgs[state][ORIENT_LEFT].elems+anim);
			}
		}
	}
	midi.start();
	return initLevel();
}
BOOL Pacman::initLevel()
{
	int w,h,i;
	WORD obj;
	BOOL pacmanHome,monsterHome;

	memcpy(level,levels[levIndex],sizeof(level));
	levPoints=0;
	pacmanHome=FALSE;
	monsterHome=FALSE;
	kSpeed=SPEED_START+levIndex;
	running=FALSE;
	strongTime=0;
	cherryTime=getCherryPeriod();
	monstCount=0;
	pacman.init(this,CREATURE_PACKMAN,STATE_WEAK,1,0);
	for(i=0;i<MONST_COUNT;i++)
		monst[i].init(	this,CREATURE_MONST,STATE_STRONG,
						monstInfo[i].speed+levIndex*SPEED_MODIF_INC,
						monstInfo[i].wait);
	for(h=0;h<LEV_H;h++)
	{
		for(w=0;w<LEV_W;w++)
		{
			obj=level[h][w];
			switch(obj)
			{	
			case OBJ_HERO_HOME:
				pacman.setHome(w,h);
				pacmanHome=TRUE;
				buildPotMap(w,h,MAP_PACMAN);
				break;
			case OBJ_MONST_HOME_1:
			case OBJ_MONST_HOME_2:
			case OBJ_MONST_HOME_3:
			case OBJ_MONST_HOME_4:
				monst[monstCount++].setHome(w,h);
				break;
			case OBJ_MONST_HOME:
				monsterHome=TRUE;
				buildPotMap(w,h,MAP_HOME);
				break;
			case OBJ_POINT:
				levPoints++;
			default:
				continue;
			}
		}
	}
	return monsterHome&&pacmanHome&&levPoints;
}
void Pacman::reset()
{
	int i;
	level[pacman.home.y][pacman.home.x]=OBJ_NONE;
	cherryTime=getCherryPeriod();
	pacman.setHome(pacman.home.x,pacman.home.y);
	pacman.time=0;
	buildPotMap(pacman.home.x,pacman.home.y,MAP_PACMAN);
	for(i=0;i<monstCount;i++)
	{
		monst[i].setHome(monst[i].home.x,monst[i].home.y);
		monst[i].time=0;
	}
	setState(STATE_WEAK);
}
BOOL Pacman::glCreate()
{
	Application::glCreate();
	midi.setWindow(window.hWnd);
	midi.open(getFileName(MUS_WEAK,MEDIA_MUS));
	// Disable 3D specific features
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	// Enable transparent colors
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	// Adjust video memory alignment
	setMemAlign(IMG_W,FALSE);
	// Set the clear color
	setClearColor(BACK_COLOR);
	font.create(window.hDC);
	return TRUE;
}
void Pacman::glDestroy()
{
	Application::glDestroy();
	midi.close();
	font.destroy();
}
void Pacman::destroy()
{
	File file;
	file.open(SCORE_FILE_NAME,_T("wb"));
	file.write(&highScore);
	file.close();
	Application::destroy();
}
void Pacman::reshape()
{
	glViewport (0, 0, window.width, window.height);
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0,window.width,
			0,window.height,
			1,-1);
	//glMatrixMode(GL_MODELVIEW);
}
BOOL Pacman::input(BYTE type)
{
	if(!Application::input(type)&&type==KEY_INPUT_MSG&&keyboard.pressed&&keyboard.waiting)
	{
		switch(keyboard.code)
		{
		case VK_ESCAPE:
			terminate();
			return TRUE;
		case VK_RETURN:
			toggleFullscreen();
			return TRUE;
		case VK_SPACE:
			running=FALSE;
			midi.pause();
			return TRUE;
		case VK_PRIOR:
			kSpeed++;
			midi.setTempo(MIDI_TEMPO*kSpeed);
			return TRUE;
		case VK_NEXT:
			if(kSpeed>levIndex)
			{
				kSpeed--;
				midi.setTempo(MIDI_TEMPO*kSpeed);
			}
			return TRUE;
		case VK_UP:
		case VK_DOWN:
		case VK_LEFT:
		case VK_RIGHT:
			if(!running&&levPoints&&!suspense)
			{
				running=TRUE;
				midi.play(NULL,MIDI_TEMPO*kSpeed);
			}
			return TRUE;
		}
	}
	return FALSE;
}
void Pacman::update()
{
	int i;
	BYTE vert=0,horiz=0;
	DWORD ms=getTimeMS(loopTime);
	
	loops++;
	runTime+=loopTime;
	setUpdated(TRUE);

	if(suspense)
	{
		susTime-=loopTime;
		if(susTime>0)
			return;
		switch(suspense)
		{
		case SUSPENSE_DEAD:
			if(!lives)
			{
				midi.close();
			}
			else
			{
				lives--;
				reset();
			}
			break;
		case SUSPENSE_LEVEL:
			if(levIndex<levCount-1)
			{
				levIndex++;
				if(!initLevel())
					terminate();
			}
			break;
		}
		suspense=SUSPENSE_NONE;
	}
	if (keyboard.keys[VK_UP]) 
	{
		vert=1;
	}
	else if (keyboard.keys[VK_DOWN]) 
	{
		vert=-1;
	}
	if (keyboard.keys[VK_RIGHT]) 
	{
		horiz=1;
	}
	else if(keyboard.keys[VK_LEFT])
	{
		horiz=-1;
	}
	pacman.setDir(horiz,vert);
	if(running)
	{
		if(pacman.state==STATE_STRONG)
		{
			if(strongTime>0)
			{
				strongTime-=ms;
			}
			else
			{
				strongTime=0;
				setState(STATE_WEAK);
			}
		}
		if(cherryTime>0)
		{
			cherryTime-=ms;
			if(cherryTime<=0)
			{
				level[pacman.home.y][pacman.home.x]=OBJ_NONE;
				cherryTime=getCherryPeriod();
			}
		}
		else
		{
			cherryTime+=ms;
			if(cherryTime>=0)
			{
				level[pacman.home.y][pacman.home.x]=OBJ_CHERRY;
				cherryTime=CHERRY_TIME;
			}
		}
		if(pacman.move(ms))
			buildPotMap(pacman.loc.x,pacman.loc.y,MAP_PACMAN);
		switch(level[pacman.loc.y][pacman.loc.x])
		{
		case OBJ_POINT:
			level[pacman.loc.y][pacman.loc.x]=OBJ_NONE;
			levPoints--;
			if(!levPoints)
			{
				running=FALSE; // Go to next level...
				midi.open(getFileName(MUS_WEAK,MEDIA_MUS));
				playSnd(SND_VICTORY);
				suspense=SUSPENSE_LEVEL;
				susTime=susTimes[SUSPENSE_LEVEL];
				running=FALSE;
			}
			else
			{
				playSnd(SND_POINT);
			}
			incScore(BONUS_POINT);
			break;
		case OBJ_BIG_POINT:
			level[pacman.loc.y][pacman.loc.x]=OBJ_NONE;
			incScore(BONUS_BIG_POINT);
			strongTime=STRONG_TIME;
			playSnd(SND_BIG_POINT);
			setState(STATE_STRONG);
			break;
		case OBJ_CHERRY:
			level[pacman.loc.y][pacman.loc.x]=OBJ_NONE;
			cherryTime=getCherryPeriod();
			incScore(BONUS_CHERRY);
			playSnd(SND_CHERRY);
			break;
		}
		for(i=0;i<monstCount;i++)
		{
			switch(monst[i].state)
			{
			case STATE_STRONG:
				setDir(monst[i],MAP_PACMAN,TRUE);
				break;
			case STATE_WEAK:
				setDir(monst[i],MAP_PACMAN,FALSE);
				break;
			case STATE_DEAD:
				setDir(monst[i],MAP_HOME,TRUE);
				break;
			}
			monst[i].move(ms);
			if(monst[i].state==STATE_DEAD)
			{
				if(level[monst[i].loc.y][monst[i].loc.x]==OBJ_MONST_HOME)
				{
					monst[i].setState(STATE_STRONG);
					monst[i].susTime=SUSPENSE_TIME;
					monst[i].time=0;
				}
			}
			else if(abs(monst[i].pos.x-pacman.pos.x)<COLLIS_TOLER&&
					abs(monst[i].pos.y-pacman.pos.y)<COLLIS_TOLER)
			{
				if(monst[i].state==STATE_WEAK)
				{
					monst[i].setState(STATE_DEAD);
					playSnd(SND_MONST_DEAD);
					incScore(BONUS_MONSTER);
				}
				else
				{
					playSnd(SND_PACKMAN_DEAD);
					setState(STATE_DEAD);
					suspense=SUSPENSE_DEAD;
					susTime=susTimes[SUSPENSE_DEAD];
					running=FALSE;
				}
			}
		}
	}
}
void Pacman::setState(BYTE state)
{
	int i;
	midi.close();
	switch(state)
	{
	case STATE_STRONG:
		pacman.setState(STATE_STRONG);
		for(i=0;i<monstCount;i++)
			if(monst[i].state==STATE_STRONG)
				monst[i].setState(STATE_WEAK);
		midi.play(getFileName(MUS_STRONG,MEDIA_MUS),MIDI_TEMPO*kSpeed);
		break;
	case STATE_WEAK:
		pacman.setState(STATE_WEAK);
		for(i=0;i<monstCount;i++)
			if(monst[i].state==STATE_WEAK)
				monst[i].setState(STATE_STRONG);
		midi.play(getFileName(MUS_WEAK,MEDIA_MUS),MIDI_TEMPO*kSpeed);
		break;
	case STATE_DEAD:
		pacman.setState(STATE_DEAD);
		break;
	}
}
BOOL Pacman::callback()
{
	switch(msg.message)
	{
		case MM_MCINOTIFY:
			if(midi.notify(msg.lParam, msg.wParam))
				return TRUE;
			break;
	}
	return FALSE;
}
void Pacman::draw()
{
	int w,h,i;
	Image *img=NULL;
	WORD obj;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	//glLoadIdentity();
	for(i=0;i<lives;i++)
		drawImg(pacman.imgs[STATE_WEAK][ORIENT_RIGHT].elems,LIFE_X,LIFE_Y+IMG_H*i);
	for(h=0;h<LEV_H;h++)
	{
		for(w=0;w<LEV_W;w++)
		{
#ifdef _DEBUG
			font.x=LEV_TO_PIX_X(w-0.5);
			font.y=LEV_TO_PIX_Y(h-0.5);
			font.color=INT_YELLOW;
			glPrint(&font,_T("%02d:%02d"),w,h);
			font.color=INT_WHITE;
			font.y+=FONT_SIZE;
			glPrint(&font,_T("%d"),maps[MAP_PACMAN][h][w]);
			font.y+=FONT_SIZE;
			glPrint(&font,_T("%d"),maps[MAP_HOME][h][w]);
#endif
			obj=level[h][w];
			switch(obj)
			{
			case OBJ_WALL:
				img=imgs+IMG_WALL;
				break;
			case OBJ_POINT:
				img=imgs+IMG_POINT;
				break;
			case OBJ_BIG_POINT:
				img=imgs+IMG_BIG_POINT;
				break;
			case OBJ_CHERRY:
				img=imgs+IMG_CHERRY;
				break;
			default:
				continue;
			}
			drawImg(img,LEV_TO_PIX_X(w-0.5),LEV_TO_PIX_Y(h-0.5));
		}
		drawImg(imgs+IMG_WALL,LEV_TO_PIX_X(w-0.5),LEV_TO_PIX_Y(h-0.5));
	}
	for(i=0;i<monstCount;i++)
		monst[i].draw();
	pacman.draw();

	font.x=TEXT_X;
	font.y=TEXT_Y;
	font.color=INT_GREEN;
	glPrint(&font,_T("LEVEL  %d/%d"),levIndex+1,levCount);
	font.y-=FONT_SIZE<<1;
	font.color=INT_CYAN;
	glPrint(&font,_T("SCORE  %d"),score);
	font.y-=FONT_SIZE;
	glPrint(&font,_T("RECORD %d"),highScore);
	font.y-=FONT_SIZE<<1;
	font.color=INT_YELLOW;
	glPrint(&font,_T("STRONG %.0f"),1e-3*strongTime);
	font.y-=FONT_SIZE;
	glPrint(&font,_T("SPEED  %d"),kSpeed);
	
	font.y-=FONT_SIZE<<1;
	font.color=INT_GREEN;
	glPrint(&font,_T("FPS:  %d"),(int)INV(loopTime));
	font.y-=FONT_SIZE;
	font.color=INT_WGREEN;
	glPrint(&font,_T("AFPS: %d"),(int)(loops/runTime));

#ifdef _DEBUG
	
	font.y-=FONT_SIZE;
	font.color=INT_WHITE;
	glPrint(&font,_T("run: %.3lf"),runTime);
	font.y-=FONT_SIZE;
	glPrint(&font,_T("cher:% d"),cherryTime);
	font.color=INT_WHITE;
	font.y-=FONT_SIZE<<1;
	glPrint(&font,_T("Points: %d"),levPoints);
	font.color=INT_WRED;
	font.y-=FONT_SIZE;
	glPrint(&font,_T("MapOp: %d"),mapop);
	font.color=INT_WHITE;
	font.y-=FONT_SIZE;
	glPrint(&font,_T("anim %d"),pacman.anim);
	font.color=INT_WHITE;
	font.y-=FONT_SIZE;
	glPrint(&font,_T("xy  %.0f:%.0f"),pacman.pos.x,pacman.pos.y);
	font.y-=FONT_SIZE;
	glPrint(&font,_T("lxy %d:%d"),pacman.loc.x,pacman.loc.y);
	font.y-=FONT_SIZE;
	glPrint(&font,_T("dxy %.0f:%.0f"),pacman.pos.x-levPixW[pacman.loc.x],pacman.pos.y-levPixH[pacman.loc.y]);
	font.y-=FONT_SIZE;
	glPrint(&font,_T("dir %+d:%+d"),pacman.dirx,pacman.diry);
	font.y-=FONT_SIZE<<1;
	font.color=INT_GRAY;
	glPrint(&font,_T("T1 %.0lf:%.0lf"),monst[0].target.x,monst[0].target.y);
	font.y-=FONT_SIZE;
	glPrint(&font,_T("T2 %.0lf:%.0lf"),monst[1].target.x,monst[1].target.y);
	font.y-=FONT_SIZE;
	glPrint(&font,_T("T3 %.0lf:%.0lf"),monst[2].target.x,monst[2].target.y);
	font.y-=FONT_SIZE;
	glPrint(&font,_T("T4 %.0lf:%.0lf"),monst[3].target.x,monst[3].target.y);
	font.color=INT_WBLUE;
	font.y-=FONT_SIZE<<1;
	glPrint(&font,_T("midi %d"),midi.getState());
	font.y-=FONT_SIZE;
	glPrint(&font,_T("error %d"),midi.getError());
	CSTR errstr=midi.getErrorStr();
	font.y-=FONT_SIZE;
	glPrint(&font,_T("%.30s"),errstr);
	font.y-=FONT_SIZE;
	glPrint(&font,_T("%.30s"),errstr+30);
	font.y-=FONT_SIZE;
	glPrint(&font,_T("%.30s"),errstr+60);
	font.y-=FONT_SIZE;
	glPrint(&font,_T("%.30s"),errstr+90);

#endif
	
}
BOOL Pacman::buildImg(	Image *newImage,
						int transpColor,
						int changeColor,
						int newColor)
{
	// build a transparent sprite with different basic color from bitmap data
	int i;
	BYTE *cs,*cd; // source and dest data pointers

	if(!newImage||!newImage->setSize(IMG_W,IMG_H,PIXEL_COMP_RGBA))
		return FALSE;
	newImage->clear();
	for(i=0,cs=buff.data,cd=newImage->data;i<IMG_PIXELS;i++,cs+=PIXEL_COMP_RGB,cd+=PIXEL_COMP_RGBA)
	{
		if(colorCmp(cs,(BYTE*)&transpColor,COLOR_TOLER))
			continue;
		if(newColor&&colorCmp(cs,(BYTE*)&changeColor,COLOR_TOLER))
		{
			memcpy(cd,(BYTE*)&newColor,PIXEL_COMP_RGBA);
			continue;
		}
		memcpy(cd,cs,PIXEL_COMP_RGB);
		cd[ALPHA]=0xff;
	}
	return TRUE;
}

};

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	Lio::Pacman app;
	return app.main(hInstance,hPrevInstance,lpCmdLine,nCmdShow);
}